Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of generic storage equations #190

Merged
merged 68 commits into from
Apr 30, 2020

Conversation

behnam-zakeri
Copy link
Contributor

@behnam-zakeri behnam-zakeri commented Apr 27, 2019

This PR is to summarize suggestions collected through discussions with @adrivinca and @JulianHunt4 in the issue #162 for representing storage equations explicitly in the model. The concept and parameters presented in this PR are illustrated in the picture below (for more info, please see the accompanying documentation in this PR):
image

This PR has done the following:

  • Tests Added
  • Documentation Added
  • Description in RELEASE_NOTES.md Added

tests/test_storage.py Outdated Show resolved Hide resolved
tests/test_storage.py Outdated Show resolved Hide resolved
tests/test_storage.py Outdated Show resolved Hide resolved
tests/test_storage.py Outdated Show resolved Hide resolved
tests/test_storage.py Outdated Show resolved Hide resolved
tests/test_storage.py Outdated Show resolved Hide resolved
tests/test_storage.py Outdated Show resolved Hide resolved
tests/test_storage.py Outdated Show resolved Hide resolved
tests/test_storage.py Outdated Show resolved Hide resolved
@behnam-zakeri
Copy link
Contributor Author

I created a document to illustrate the storage concept presented in this PR and discuss the new equations suggested to be added to the model. I put it under a "temporary" folder in the doc folder. Please feel free to put in the right place.

RELEASE_NOTES.md Outdated Show resolved Hide resolved
tests/test_storage.py Outdated Show resolved Hide resolved
tests/test_storage.py Outdated Show resolved Hide resolved
tests/test_storage.py Outdated Show resolved Hide resolved
tests/test_storage.py Outdated Show resolved Hide resolved


# A function for adding sub-annual time steps to a MESSAGEix model
def add_seasonality(scen, seasons_dict):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@behnam2015 do you think it would be worth it to make this a top-level message_ix.Scenario function? Right now we have an add_horizon() function which helps you set up a model. I think it would be great to have this somewhere callable by others as well! It would need to do all the required work though, so something like the year_to_time below as well.

Copy link
Contributor Author

@behnam-zakeri behnam-zakeri May 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gidden, thanks for the suggestion! I agree it will be a good addition. @adrivinca also suggested that the order of each time step can be specified through a set. Now, we specifiy the time order through a parameter: time_seq. So, add_season or something similar can be optionally used to wrap the information related to

  1. a subannual time step (set time)
  2. hirerachy of that time step (set lvl_temporal and map_temporal_hierarchy)
  3. Duration of that time step (parameter duration_time)
  4. The order of that time step in a year (in this PR, parameter time_seq)

# A function for initiating sets/parameters required for storage equations
# NOTICE: this function can be deleted when storage parameters/sets are added
# to ixmp java backend
def init_storage(scen):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we actually have support for this already in the code base~! Check out this line. I would suggest putting this there (feel free to stop by and I can show you how). Then when it gets into ixmp, we can take it out again.

On the other hand, I would also be happy if we wanted to move this work all into python, but I'm not sure that's in the scope of this PR =)

Copy link
Contributor Author

@behnam-zakeri behnam-zakeri Apr 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @gidden for the explanation and the PR! I merged your suggestions.

gidden
gidden previously requested changes Apr 29, 2019
Copy link
Member

@gidden gidden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! A few thoughts:

  • it seems like the unit tests are not passing due to GAMS issues. can you replicate these on your machine?
  • can you please transfer the text/images from doc/temporary/Implementation of storage equations in MESSAGEix framework.docx to the GAMS code, please? This may require some additional work on our documentation scraping tool as I am pretty sure we don't support images yet... but take a look and let me know how it goes
  • it is not clear why the STORAGE_EQUALITY constraint is needed (I probably need a bit more understanding here). shouldn't the storage unit be allowed to have different quantities at the beginning and end of each time step?
  • in general the test looks great; however, I must admit I really don't understand all parts of it. I would happily sit down and have you explain it to me!

@adrivinca
Copy link
Contributor

adrivinca commented Apr 30, 2019

Thanks @behnam2015 for the PR, I only have some comments and suggestions on the GAMS side.

period_parameter_assignment.gms
I tried the model with different naming for the 'time' elements (i.e non alphabetic order and with different numbers of letters) and there is a problem on the Python side, list(set(seasons_dict.keys())) is re-ordering the set alphabetically, this should not happen, so that one can have seasons like winter, summer and so on.
Fixing that (but I did it hard coding), then the new ordering is correct on the GAMS side.
However, I would suggest making time_seq a set and defining some basic sets that can be helpful in the future, such as first_time(time) last_time(time).

This is an example (not tested), but if we agree I can push changes to your branch:

Sets
first_time(time)
last_time(time)
;

first_time(time)$( ORD(time)$time_seq(time) = 1) = yes ;
last_time(time)$( ORD(time)$time_seq(time) = CARD(time_seq(time) ) = yes ;

* mapping of first and last time steps in a period AV
map_time_first_last(year_all,year_all2,time,time2)$( ( ORD(year_all) = ORD(year_all2)) AND
    first_time(time) AND  last_time(time) ) = yes;

Concerning STORAGE_CHANGE equation, if the level_storage is always different from the normal commodity levels (e.g charger output = discharger input but different than charger input) then we don't need all the charger and discharger mappings and we can write a single line:

STORAGE_CHANGE(node,storage_tec,level,year,time)$( SUM( (mode,tec,commodity), map_tec_storage_level(node,tec,storage_tec,level,year,time) AND
    (map_tec_charge(node,tec,mode,commodity,level,year,time) OR map_tec_discharge(node,tec,mode,commodity,level,year,time) ) ) )..
* change in the content of storage in the examined timestep
    STORAGE_CHG(node,storage_tec,level,year,time) =E=
* increase in the content of storage due to the activity of charging technologies - decrease due to discharging
        SUM((location,vintage,mode,tec,commodity,time2)$(map_tec_lifetime(node,tec,vintage,year) AND ) AND map_tec_storage_level(node,tec,storage_tec,level,year,time) ),
      (  output(location,tec,vintage,year,mode,node,commodity,level,time2,time) - input(location,tec,vintage,year,mode,node,commodity,level,time2,time) ) * ACT(location,tec,vintage,year,mode,time) )

but if we don't want to be so strict, and keep a level of freedom, I think the current implementation is ok.

in STORAGE_BALANCE Eq the if condition should not be with storage_loss otherwise the equation does not work then no losses is defined, probably map_tec_storage_level works well

Finally bound_storage_up and lo have the should have the same indexes as the STORAGE variable.
So we should decide whether keeping the commodity index in STORAGE or removing it from the boundary parameters

if we agree on some of these points I can also contribute to the code

tests/test_storage.py Outdated Show resolved Hide resolved
@behnam-zakeri
Copy link
Contributor Author

behnam-zakeri commented May 13, 2019

Thanks a lot @gidden for the feedback and useful suggestions. I will move the documentation to the GAMS files as soon as we are happy with the final equations.
As for STORAGE_EQUALITY, based on the feedback from you and others, I replaced this with an optional parameter called relation_storage that can relate the content of storage between two time steps (and even between two different periods). This is now represented in equation STORAGE_REL as an inaquality to offer more flexiblity (vs. equality that forced the same content). I will continue improving the PR if you have further comments.

@behnam-zakeri
Copy link
Contributor Author

behnam-zakeri commented May 13, 2019

Thanks a lot @adrivinca for feedback and suggestions. I try to address them one by one:

  • related to period_parameter_assignment and time_seq, I'm quite open if we decide to go for a set for adding the time order. Now, after removing STORAGE_EQUALITY we don't need to specify or map the first and last time steps anymore.
  • related to list(set(seasons_dict.keys())), the order doesn't affect the results, as the duration will be specified for each time step directly in the loop.
  • related to STORAGE_BALANCE, it is now mapped based on map_tec_storage_level.
  • commodity was removed from the bounds and storage_loss.
    Please have a look again and suggest improvements if needed.

@gidden
Copy link
Member

gidden commented May 28, 2019

Hi all - just getting back and catching up. @behnam2015, does this need another review now?

@behnam-zakeri
Copy link
Contributor Author

@gidden, nothing from my side, @adrivinca may want to suggest some additions.

@gidden
Copy link
Member

gidden commented Jun 21, 2019

Hi folks, it looks like we have some conflicts in the GAMS code. I would suggest @behnam2015 @adrivinca and @danielhuppmann decide whether this is worth trying to get into the next release or should be a first PR into master after the release is finalized. I'm happy either way if all agree.

tests/test_storage.py Outdated Show resolved Hide resolved
tests/test_storage.py Outdated Show resolved Hide resolved
@behnam-zakeri
Copy link
Contributor Author

@behnam-zakeri I had to make the modifications in [iiasa/ixmp#315] ... Can you (1) please check that the code there, in combination with this branch, allows test_legacy_version to pass (it does for me), and (2) if so, leave an approving review there?

Thanks @khaeru for resolving this. Yes, both tests (storage and legacy) pass after these changes.

@francescolovat
Copy link
Contributor

Unfortunately a test from message_ix/tests/test_tutorials.py::test_tutorial[austria_multiple_policies-answers] from the MacOS image is still failing:

  File "/Users/travis/miniconda/envs/testing/lib/python3.8/site-packages/nbconvert/preprocessors/execute.py", line 448, in preprocess_cell

    raise CellExecutionError.from_cell_and_msg(cell, out)

nbconvert.preprocessors.execute.CellExecutionError: An error occurred while executing the following cell:

------------------

new_scen = 'interest_rate_.1'

with make_scenario(mp, country, name, base_scen, new_scen) as scenario:

    scenario.add_par("interestrate", horizon, value=.1, unit='%')

@khaeru
Copy link
Member

khaeru commented Apr 30, 2020

Unfortunately a test from message_ix/tests/test_tutorials.py::test_tutorial[austria_multiple_policies-answers] from the MacOS image is still failing

@francescolovat good catch—see further down at line 1827:

com.zaxxer.hikari.pool.HikariPool.PoolInitializationException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to initialize pool: Database lock acquisition failure: lockFile: org.hsqldb.persist.LockFile@5035d5ba[file =/private/var/folders/nz/vv4_9tw56nv9k3tkvyszvwg80000gn/T/pytest-of-travis/pytest-0/localdb/default.lck, exists=true, locked=false, valid=false, ] method: checkHeartbeat read: 2020-04-30 15:54:05 heartbeat - read: -9763 ms.
com.zaxxer.hikari.pool.HikariPool.PoolInitializationException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to initialize pool: Database lock acquisition failure: lockFile: org.hsqldb.persist.LockFile@5035d5ba[file =/private/var/folders/nz/vv4_9tw56nv9k3tkvyszvwg80000gn/T/pytest-of-travis/pytest-0/localdb/default.lck, exists=true, locked=false, valid=false, ] method: checkHeartbeat read: 2020-04-30 15:54:05 heartbeat - read: -9763 ms.

This is a failure in the Java database layer that happens randomly once in a while; it is spurious (doesn't signal a code error). I will restart this job.

@francescolovat
Copy link
Contributor

Ok @khaeru thanks! 🙂

@codecov
Copy link

codecov bot commented Apr 30, 2020

Codecov Report

Merging #190 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master     #190   +/-   ##
=======================================
  Coverage   88.95%   88.95%           
=======================================
  Files          30       30           
  Lines        2697     2697           
=======================================
  Hits         2399     2399           
  Misses        298      298           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 34ac6bf...34ac6bf. Read the comment docs.

@behnam-zakeri
Copy link
Contributor Author

behnam-zakeri commented Apr 30, 2020

Thanks @francescolovat for organizing the documentation very nicely and @khaeru for improving the model initialization workflow. I have no further additions or edits related to the formulation.

Copy link
Member

@khaeru khaeru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no further additions or edits related to the formulation.

A major contribution, @behnam-zakeri—thanks to you and all others who added their code and inputs!

@khaeru khaeru dismissed gidden’s stale review April 30, 2020 18:39

These comments were addressed by the author over the past year

@khaeru khaeru merged commit 882ffe2 into iiasa:master Apr 30, 2020
@francescolovat francescolovat self-requested a review May 1, 2020 18:12
@behnam-zakeri behnam-zakeri added enh New features & functionality timeslice labels Jun 18, 2020
@khaeru khaeru mentioned this pull request Sep 30, 2021
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enh New features & functionality timeslice
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants